package zvalidation

import (
	"fmt"
	"reflect"
	"regexp"

	"gitee.com/youkelike/zerror"
)

// 定义验证函数类型
type ValidatorFunc func(value any) error

// 定义验证规则类型
type Rules map[string]ValidatorFunc

func ValidateAllFields(obj any, rules Rules) error {
	return ValidateSelectedFields(obj, rules, GetExportedFieldNames(obj)...)
}

// 通用校验函数
func ValidateSelectedFields(obj any, rules Rules, fields ...string) error {
	// 通过反射获取结构体的值和类型
	objValue := reflect.ValueOf(obj)
	objType := reflect.TypeOf(obj)

	if objType.Kind() == reflect.Ptr {
		objValue = objValue.Elem()
		objType = objType.Elem()
	}
	if objType.Kind() != reflect.Struct {
		return fmt.Errorf("expected a struct, got %s", objType.Kind())
	}

	// 遍历需要校验的字段
	for _, field := range fields {
		// 跳过不存在或未导出的字段
		structField, exists := objType.FieldByName(field)
		if !exists || !structField.IsExported() {
			continue
		}

		// 提取字段值
		fieldValue := objValue.FieldByName(field)
		if fieldValue.Kind() == reflect.Ptr {
			if fieldValue.IsNil() {
				// 如果是指针类型但为 nil，跳过验证
				continue
			}
			// 指针不为 nil，解引用
			fieldValue = fieldValue.Elem()
		}

		// 获取字段的校验规则
		validator, ok := rules[field]
		if !ok {
			continue // 没有校验规则跳过
		}

		if err := validator(fieldValue.Interface()); err != nil {
			return err
		}
	}

	return nil
}

// GetExportedFieldNames 返回传入结构体中所有可导出的字段名字.
func GetExportedFieldNames(obj any) []string {
	val := reflect.ValueOf(obj)
	typ := reflect.TypeOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	if typ.Kind() != reflect.Struct {
		return []string{}
	}

	var fieldNames []string
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		if field.IsExported() { // 从 Go 1.17 开始提供
			fieldNames = append(fieldNames, field.Name)
		}
	}

	return fieldNames
}

var (
	lengthRegex = regexp.MustCompile(`^.{3,20}$`)                                        // 长度在 3 到 20 个字符之间
	validRegex  = regexp.MustCompile(`^[A-Za-z0-9_]+$`)                                  // 仅包含字母、数字和下划线
	letterRegex = regexp.MustCompile(`[A-Za-z]`)                                         // 至少包含一个字母
	numberRegex = regexp.MustCompile(`\d`)                                               // 至少包含一个数字
	emailRegex  = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) // 邮箱格式
	phoneRegex  = regexp.MustCompile(`^1[3-9]\d{9}$`)                                    // 中国手机号
)

func isValidUsername(username string) bool {
	// 校验长度
	if !lengthRegex.MatchString(username) {
		return false
	}
	// 校验字符合法性
	if !validRegex.MatchString(username) {
		return false
	}
	return true
}

func isValidPassword(password string) error {
	switch {
	// 检查新密码是否为空
	case password == "":
		return zerror.ErrInvalidArgument.WithMessage("password cannot be empty")
	// 检查新密码的长度要求
	case len(password) < 6:
		return zerror.ErrInvalidArgument.WithMessage("password must be at least 6 characters long")
	// 使用正则表达式检查是否至少包含一个字母
	case !letterRegex.MatchString(password):
		return zerror.ErrInvalidArgument.WithMessage("password must contain at least one letter")
	// 使用正则表达式检查是否至少包含一个数字
	case !numberRegex.MatchString(password):
		return zerror.ErrInvalidArgument.WithMessage("password must contain at least one number")
	}
	return nil
}

func isValidEmail(email string) error {
	// 检查电子邮件地址格式
	if email == "" {
		return zerror.ErrInvalidArgument.WithMessage("email cannot be empty")
	}

	// 使用正则表达式校验电子邮件格式
	if !emailRegex.MatchString(email) {
		return zerror.ErrInvalidArgument.WithMessage("invalid email format")
	}

	return nil
}

func isValidPhone(phone string) error {
	// 检查手机号码格式
	if phone == "" {
		return zerror.ErrInvalidArgument.WithMessage("phone cannot be empty")
	}

	// 使用正则表达式校验手机号码格式（假设是中国手机号，11位数字）
	if !phoneRegex.MatchString(phone) {
		return zerror.ErrInvalidArgument.WithMessage("invalid phone format")
	}

	return nil
}
